﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;

namespace TaskFramework.Tests
{
    public class QuickTaskFactory : ITaskFactory
    {
        public Type TaskRequestType
        {
            get { return typeof(QuickTaskRequest); }
        }

        public Task CreateTask(object requestData)
        {
            return new QuickTask(requestData);
        }
    }

    public class QuickTaskRequest
    {
        private readonly bool _throwExceptionBeforeRouting;
        private readonly int _routeCount;
        private readonly bool _throwExceptionAfterRouting;
        private readonly RouteType _routeType;
        private readonly int _delayMilliseconds;

        public QuickTaskRequest()
            : this(0, Tests.RouteType.RouteSingleRequestAndDecrementRouteCount, 0, false)
        {
        }

        public QuickTaskRequest(int routeCount, RouteType routeType, int delayMilliseconds, bool throwExceptionAfterRouting)
        {
            _routeCount = routeCount;
            _delayMilliseconds = delayMilliseconds;
            _routeType = routeType;
            _throwExceptionAfterRouting = throwExceptionAfterRouting;
        }

        public QuickTaskRequest(bool throwExceptionBeforeRouting)
        {
            _throwExceptionBeforeRouting = throwExceptionBeforeRouting;
        }

        public QuickTaskRequest(QuickTaskRequest request, int routeCount)
        {
            _routeCount = routeCount;
            _routeType = request._routeType;
            _delayMilliseconds = request._delayMilliseconds;
            _throwExceptionAfterRouting = request._throwExceptionAfterRouting;
            _throwExceptionBeforeRouting = request._throwExceptionBeforeRouting;
        }

        public int RouteCount
        {
            get { return _routeCount; }
        }

        public bool ThrowExceptionBeforeRouting
        {
            get { return _throwExceptionBeforeRouting; }
        }

        public bool ThrowExceptionAfterRouting
        {
            get { return _throwExceptionAfterRouting; }
        }

        public RouteType RouteType
        {
            get { return _routeType; }
        }

        public int DelayMilliseconds
        {
            get { return _delayMilliseconds; }
        }
    }

    public class QuickTask : Task
    {
        private readonly QuickTaskRequest _requestData;
        private int _remainingRouteCount = -1; // needed to retain state when task is transfered to pool.

        public QuickTask(object requestData)
        {
            _requestData = (QuickTaskRequest)requestData;
        }

        public override object RequestData
        {
            get { return _requestData; }
        }

        public override void Execute(RoutingHelper router)
        {
            if (_requestData.ThrowExceptionBeforeRouting)
            {
                throw new InvalidOperationException("Thrown before routing.");
            }

            if (_remainingRouteCount == -1)
            {
                _remainingRouteCount = _requestData.RouteCount;
            }

            if (_remainingRouteCount > 0)
            {
                if (_requestData.RouteType == RouteType.RouteRequestInBatchAndSetRouteCountToZero)
                {
                    for (int i = 0; i < _requestData.RouteCount; i++)
                    {
                        QuickTaskRequest request = new QuickTaskRequest(_requestData, 0);
                        router.RouteRequest(request);
                    }
                }
                else if (_requestData.RouteType == RouteType.RouteSingleRequestAndDecrementRouteCount)
                {
                    QuickTaskRequest request = new QuickTaskRequest(_requestData, _requestData.RouteCount - 1);
                    router.RouteRequest(request);
                }
                else if (_requestData.RouteType == RouteType.TransferTaskToPool)
                {
                    _remainingRouteCount--;
                    Debug.WriteLine("Transferring task to pool. ");
                    router.TransferToPool(this);
                    return;
                }
            }

            if (_requestData.DelayMilliseconds > 0)
            {
                Thread.Sleep(_requestData.DelayMilliseconds);
            }

            if (_requestData.ThrowExceptionAfterRouting)
            {
                throw new InvalidOperationException("Thrown after routing.");
            }
        }
    }
}
